package asteroids.model;

import asteroids.ModelException;
import asteroids.Util;
import asteroids.util.Vector;
import be.kuleuven.cs.som.annotate.*;

/**
 * A class representing a bullet in the project asteroid.
 * 
 * Each bullet has a source, which is the ship that has fired this bullet. The mass of the bullet is calculated bases
 * upon it's radius and it's density.
 * 
 * @author Michiel De Cuyper & Lennart De Graef
 * @version 3.0
 * 
 * @invar The mass of this Bullet is equal to the constant value BULLETMASS
 *        | this.getMass() == BULLETMASS == private final double BULLETMASS = 4 / 3 * Math.PI *
 *        Math.pow(this.getRadius(), 3) * BULLETDENSITY;
 * 
 */
public class Bullet extends SpaceObject {

	/**
	 * @param radius
	 *            The radius of this asteroid
	 * @param velocity
	 *            The velocity of this asteroid, a vector.
	 * @param position
	 *            The position of this asteroid, a vector.
	 * @param world
	 *            The world in which this asteroid is.
	 * @param source
	 *            The ship that fired this bullet
	 * @throws ModelException
	 *             Throws a modelexception whenever the radius, position or world is not valid, or when the source is
	 *             null
	 *             | if( !isValidRadius(radius) || this.source == null || !isValidPosition(position) ||
	 *             !isValidWorld(world)) throw new ModelException(errorMessage)
	 * 
	 * @effect Creates a Bullet, calling the constructor of SpaceObject
	 *         | super(radius, velocity, position, world)
	 * @effect Sets the mass of this Bullet equal to the BulletMass
	 *         | this.setMass(this.getAsteroidMass())
	 * @effect Sets the source of this ship to the given source, when the source is not null
	 *         | if(isValidSource(source)) this.setSource(source)
	 */
	public Bullet(double radius, Vector velocity, Vector position, World world, Ship source) throws ModelException {
		super(radius, velocity, position, world);
		if (!isValidSource(source)) throw new ModelException("Source is null");
		else this.setSource(source);
		this.setMass(getBulletMass());
	}

	/**
	 * Returns the source of this bullet
	 * 
	 * @return The source of this bullet
	 *         | return this.source
	 */
	@Basic
	@Immutable
	public Ship getSource() {
		return this.source;
	}

	/**
	 * Checks if a given source is a valid source
	 * 
	 * @param source
	 *            The source that will be checked
	 * @return True if and only if the source is not null
	 *         | return source != null
	 */
	public static boolean isValidSource(Ship source) {
		return source != null;
	}

	/**
	 * Sets the source of this bullet to a given ship
	 * 
	 * @param source
	 *            The ship that fired this bullet
	 */
	@Raw
	public void setSource(Ship source) {
		this.source = source;
	}

	/**
	 * A ship variable, storing the source of this bullet. This is the ship that fired the bullet
	 */
	private Ship source;

	/**
	 * Returns the mass of a bullet, which is a constant
	 * 
	 * @return The mass of this bullet
	 *         | return this.BULLETMASS
	 */
	public double getBulletMass() {
		return this.BULLETMASS;
	}

	/**
	 * Checks wether a given mass is valid, i.e. the mass is equals to the bulletmass
	 * 
	 * @return True if and only if the given mass equals the bulletmass
	 *         | return mass == BULLETMASS
	 */
	@Override
	public boolean isValidMass(double mass) {
		return (Util.fuzzyEquals(mass, BULLETMASS));
	}

	/**
	 * 2 final doubles, storing the density of a bullet and the mass of a bullet
	 */
	private final double BULLETDENSITY = 7.8 * Math.pow(10, 12);
	private final double BULLETMASS = 4 / 3 * Math.PI * Math.pow(this.getRadius(), 3) * BULLETDENSITY;

	/**
	 * Checks wether a given radius is valid
	 * 
	 * @return True if and only if the given radius equals the bullet radius
	 *         | return radius == getBulletRadius;
	 */
	@Override
	public boolean isValidRadius(double radius) {
		return radius == getBulletRadius();
	}

	/**
	 * Returns the radius a bullet has. For now, this is a static number
	 * 
	 * @return The value of the radius of the bullet (being 3)
	 *         | return 3;
	 */
	public static double getBulletRadius() {
		return 3;
	}

	/**
	 * Returns true if the bullet has bounced against a wall
	 * 
	 * @return The value of the boolean variable 'bounced', which is true when a bullet has hit a wall
	 *         | return bounced
	 */
	public boolean hasBounced() {
		return bounced;
	}
	
	/**
	 * Sets the value of bounced to true
	 * 
	 * @post The value of bounced equals true
	 * 		| this.hasBounced == true;
	 */
	public void setBounced() {
		this.bounced = true;
	}
	
	/**
	 * A boolean variable, storing wether a bullet has bounced already or not.
	 */
	private boolean bounced = false;

}
